
#装饰器

#我们在开发一个项目的时候，需要尽量遵循这样一个规则：修改是封闭的，扩展是开放的；也就是说在后期更改需求的时候，我们可以
#不改动以前的代码，而只需要通过扩展就可以满足新需求。Python的装饰器就是帮助我们来实现这一个目标的方法之一。

#一、什么是装饰器
#通过一个示例来理解装饰器的作用，首先定义一个打印当前时间的函数
import time
def printCurrentTime():
    currentTime = time.time()
    print("当前时间戳：" + str(currentTime))
    
printCurrentTime()  #打印：当前时间戳：1525522986.449398

#考虑这样一个问题：假设我们要增强printCurrentTime()函数的功能，
#比如，在函数调用前后自动打印日志，但又不希望修改printCurrentTime()函数的定义，
#这种在代码运行期间动态增加功能的方式，称之为“装饰器”（Decorator）。


#二、实现一个装饰器
#我们使用装饰器来解决上述的问题，装饰器其实就是一个返回函数的高阶函数。下面我们定义一个能打印日志的decorator，
#装饰器函数pringLog接收一个函数作为参数，并返回一个函数，代码如下：
def pringLog(func):
    def wrapper(*args, **kw):  
        print('---开始调用函数： %s():' % func.__name__)
        result = func(*args, **kw)
        print('---结束调用函数： %s():' % func.__name__)
        return result
    return wrapper

#注意：args与kw是Python中的可变参数，同时使用时，*args在前**kwargs在后
# *args表示任何多个无名参数，它是一个tuple；
# **kwargs表示关键字参数，它是一个dict。


#然后我们需要借助Python的@语法，把decorator置于需要增加答应log功能的函数的定义处如下：
@pringLog
def printCurrentTime2():
    currentTime = time.time()
    print("当前时间戳：" + str(currentTime))

#测试printCurrentTime2的打印效果
printCurrentTime2()
'''
---开始调用函数： printCurrentTime2():
当前时间戳：1525523570.290186
---结束调用函数： printCurrentTime2():
'''
#分析：pringLog()是一个decorator，返回一个函数，
#原来的printCurrentTime2()函数仍然存在，只是现在同名的printCurrentTime2变量指向了新的函数。




#三、装饰器中也传入参数
#定义一个高阶函数，返回装饰器的函数
def printLogWithText(text):
    def pringLog(func):
        def wrapper(*args, **kw):  
            print(text + '---开始调用函数： %s():' % func.__name__)
            result = func(*args, **kw)
            print(text + '---结束调用函数： %s():' % func.__name__)
            return result
        return wrapper
    return pringLog

#使用装饰器
@printLogWithText("装饰器参数")
def printCurrentTime3():
    currentTime = time.time()
    print("当前时间戳：" + str(currentTime))

#测试printCurrentTime3的打印效果
printCurrentTime3()

'''
装饰器参数---开始调用函数： printCurrentTime3():
当前时间戳：1525671291.8881621
装饰器参数---结束调用函数： printCurrentTime3():
'''

#四、装饰器引发的函数名问题
#经过装饰器后，如果我们在外部打印被装饰的函数名，就会发现它们的名字变化了
print(printCurrentTime2.__name__)  #打印：wrapper
print(printCurrentTime3.__name__)  #打印：wrapper

#解决方法：使用Python内置的functools.wraps设置装饰函数
#相当于wrapper.__name__ = func.__name__
import functools
def printInfo(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):  
        print('---开始调用函数： %s():' % func.__name__)
        result = func(*args, **kw)
        print('---结束调用函数： %s():' % func.__name__)
        return result
    return wrapper

@printInfo
def test():
    pass


print(test.__name__)  #打印test